package de.uni_saarland.coli.doclists.suffixtree.memory;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map.Entry;

import de.uni_saarland.coli.doclists.document.IndexDocument;
import de.uni_saarland.coli.doclists.index.Index;
import de.uni_saarland.coli.doclists.suffixtree.IEdgeManager;
import de.uni_saarland.coli.doclists.suffixtree.STreeEdge;

/**
 * 
 * @author stefan
 * 
 *         manages edges
 */
public class MemoryEdgeManager implements IEdgeManager {
	/**
	 * the edges
	 */
	//private MemoryMapData<STreeEdge> edgeMap;
	private HashMap<Integer,STreeEdge> edgeMap;

	/**
	 * Constructor
	 */
	public MemoryEdgeManager() {
		this.edgeMap = new HashMap<Integer, STreeEdge>();
	}
	public STreeEdge createEdgeReplace(int docID, int startToken, int endToken, int startNode) {
		IndexDocument doc = Index.getDocumentManager().getDocument(docID);

		if (doc == null)
			return null;

		int token = doc.getTermAt(startToken);

		STreeEdge e = find(startNode, token);

		if (e != null)
			return e;

		e = new STreeEdge(docID, startToken, endToken, startNode, token);

		int h = hash(startNode, token);

		while (edgeMap.get(h) != null)
			h++;

		edgeMap.put(h, e);

		return e;
	}


	@Override
	public STreeEdge createEdge(int docID, int startToken, int endToken,
			int startNode) {
		IndexDocument doc = Index.getDocumentManager().getDocument(docID);

		if (doc == null)
			return null;

		int token = doc.getTermAt(startToken);

		STreeEdge e = find(startNode, token);

//		if (e != null)
	//		return e;

		e = new STreeEdge(docID, startToken, endToken, startNode, token);

		int h = hash(startNode, token);

		while (edgeMap.get(h) != null)
			h++;

		edgeMap.put(h, e);

		return e;
	}
	
	public void remove(STreeEdge edge) {
        int h = hash(edge.getStartNode(), edge.getFirstToken());
        while( edgeMap.get(h).getStartNode() != edge.getStartNode() ||
        		edgeMap.get(h).getFirstToken() != edge.getFirstToken())
        	h++;
        for (; ;) {
        	//edgeMap.get(h).setStartNode(-1);
        	int k = h;
        	for (;;) {
        		h++;
        		if (edgeMap.get(h) == null || edgeMap.get(h).getStartNode() == -1)
        			return;
        		int l = hash(edgeMap.get(h).getStartNode(), edgeMap.get(h).getFirstToken());
        		if (h >= l && l > k)
                    continue;
                if (l > k && k > h)
                    continue;
                if (l > h && h >= k)
                    continue;
                break;
        	}
        	edgeMap.put(k, edgeMap.get(h));
        	edgeMap.remove(h);
        }
	}

	/**
	 * Creates a hash code
	 * 
	 * @param startNode
	 *            the start node
	 * @param token
	 *            the start token
	 * @return the hash
	 */
	private int hash(int startNode, int token) {
		return (startNode << 16) + token;
	}

	@Override
	public STreeEdge find(int startNode, int token) {
		int h = hash(startNode, token);

		while (true) {
			STreeEdge e = edgeMap.get(h);

			if (e == null)
				return null;

			if (startNode != e.getStartNode() || token != e.getFirstToken())
				h++;
			else {
				return e;
			}
		}
	}

	@Override
	public void save(String path, String baseName) {
		FileOutputStream fos = null;
		ObjectOutputStream out = null;

		try {
			fos = new FileOutputStream(path + "/" + baseName + ".edgeMap");
			out = new ObjectOutputStream(fos);
			out.writeObject(this.edgeMap);
			out.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void open(String path, String baseName) {
		this.edgeMap = null;

		FileInputStream fis = null;
		ObjectInputStream in = null;

		try {
			fis = new FileInputStream(path + "/" + baseName + ".edgeMap");
			in = new ObjectInputStream(fis);
			this.edgeMap = (HashMap<Integer, STreeEdge>) in.readObject();
			in.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (Entry<Integer,STreeEdge> iter :this.edgeMap.entrySet()) {
			sb.append(iter.getValue().toString()).append("\n");
			sb.append("End Node\n");
			sb.append(Index.getNodeMap().getNode(iter.getValue().getEndNode()));
		}
		return sb.toString();
	}
	
	public STreeEdge removeEdge(STreeEdge edge) {
		int h = hash(edge.getStartNode(), edge.getFirstToken());
		this.edgeMap.remove(h);
		return edge;
	}
	public STreeEdge addEdge(STreeEdge edge) {
		int h = hash(edge.getStartNode(), edge.getFirstToken());
		this.edgeMap.put(h,edge);
		return edge;
	}

}
